/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.server;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

/**
 * This is a utility class which will attempt to look up the desired username
 * and password in the specified Active Directory.
 * 
 * NOTE: This class needs to be configured before it is usable.
 * 
 * @author serge
 * 
 */
public class ServerLDAPAuthentication {

	public static String adminName, adminName_noCN, adminPassword, urlDC,
			usernameTag, searchBase;

	/**
	 * Attempt to retreive the first name and last name.
	 * @param username
	 * @param password
	 * @return
	 */
	public static boolean authenticate(String username, String password) {
		Hashtable envDC = new Hashtable();

		envDC.put(Context.INITIAL_CONTEXT_FACTORY,
				"com.sun.jndi.ldap.LdapCtxFactory");

		envDC.put(Context.SECURITY_AUTHENTICATION, "simple");
		envDC.put(Context.SECURITY_PRINCIPAL, adminName);
		envDC.put(Context.SECURITY_CREDENTIALS, adminPassword);

		// connect to both a GC and DC
		envDC.put(Context.PROVIDER_URL, urlDC);

		// We need to chase referrals when retrieving attributes from the DC
		// as the object may be in a different domain
		envDC.put(Context.REFERRAL, "follow");

		try {
			// Create the initial directory context for both DC and GC
			LdapContext ctxDC = new InitialLdapContext(envDC, null);

			// Create the search controls
			SearchControls searchCtls = new SearchControls();

			// Specify the attributes to return
			String returnedAtts[] = { "sn", usernameTag };
			searchCtls.setReturningAttributes(returnedAtts);

			// Specify the search scope
			searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

			// specify the LDAP search filter
			String searchFilter = "(" + usernameTag + "=" + username + ")";

			// initialize counter to total the results
			int totalResults = 0;

			// Search for objects in the GC using the filter
			NamingEnumeration answer = ctxDC.search(searchBase, searchFilter,
					searchCtls);

			// Loop through the search results
			while (answer.hasMoreElements()) {
				SearchResult sr = (SearchResult) answer.next();

				totalResults++;

				System.out.println(">>>" + sr.getName());
				return reauthenticate(sr.getName(), password, username);
			}

			System.out.println("Total results: " + totalResults);
			ctxDC.close();

		} catch (NamingException e) {
			System.err.println("Problem searching directory: " + e);
		}
		return false;
	}

	public static boolean reauthenticate(String CN, String password,
			String username) {

		System.out.println("Reauthenticating");

		// set up one connection for the Global Catalog port; 3268
		// and another for the normal LDAP port; 389
		Hashtable envDC = new Hashtable();

		String adminName = CN + "," + adminName_noCN;
		String adminPassword = password;

		envDC.put(Context.INITIAL_CONTEXT_FACTORY,
				"com.sun.jndi.ldap.LdapCtxFactory");

		envDC.put(Context.SECURITY_AUTHENTICATION, "simple");
		envDC.put(Context.SECURITY_PRINCIPAL, adminName);
		envDC.put(Context.SECURITY_CREDENTIALS, adminPassword);

		// connect to both a GC and DC
		envDC.put(Context.PROVIDER_URL, urlDC);

		// We need to chase referrals when retrieving attributes from the DC
		// as the object may be in a different domain
		envDC.put(Context.REFERRAL, "follow");

		try {

			// Create the initial directory context for both DC and GC
			LdapContext ctxDC = new InitialLdapContext(envDC, null);

			// Now perform a search against the GC
			// Create the search controls
			SearchControls searchCtls = new SearchControls();

			// Specify the attributes to return
			String returnedAtts[] = { "sn", usernameTag };
			searchCtls.setReturningAttributes(returnedAtts);

			// Specify the search scope
			searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

			// specify the LDAP search filter
			String searchFilter = "(" + usernameTag + "=" + username + ")";

			// Search for objects in the GC using the filter
			NamingEnumeration answer = ctxDC.search(searchBase, searchFilter,
					searchCtls);

			// check if we could query ourselves.
			if (answer.hasMoreElements()) {
				System.out.println("SUCCESS!");
				ctxDC.close();
				return true;
			}

		} catch (NamingException e) {
			System.err.println("Problem searching directory: " + e);
		}
		return false;
	}
}
